
#include "Parser.h"
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <cstring>
#include "../utils/Constantes.h"
#include <time.h>

#define EXTENSION_FILES_TO_INDEX ".txt"

using std::cerr;
using std::cout;
using std::endl;
using std::string;
using std::stringstream;
using std::ifstream;
using std::fstream;

static const short int EMPTY_BUFFER = 0;
static const short int READING_WORD = 1;
static const short int READING_NUMBER = 2;

static const short unsigned int MIN_DIGITS_TO_INDEX = 3;
static const short unsigned int MAX_DIGITS_TO_INDEX = 4;

static const short int WHITESPACE = 0;
static const short int DIGIT = 1;
static const short int LETTER = 2;
static const short int TITTLE = 3;
static const short int OTHER = 4;

short int Parser::getCharClass(char c) {
	int auxC = (int) c;
	if ( ('A' <= c && c <= 'Z') || //mayusculas
			('a' <= c && c <= 'z') ) { // minusculas
		return LETTER;
	} else if ( (8 <= auxC && auxC <= 13) ||  // all tabs
			(auxC == 32)) { // 32 = space
		return WHITESPACE;
	} else if ('0' <= c && c <= '9') {
		return DIGIT;
	} else if (auxC == -61) {
		return TITTLE;
	} else {
		return OTHER;
	}
}

Parser::Parser() {
	document = 0;
	position = 1;
	setState(EMPTY_BUFFER);
}

void Parser::parseDocument(char*  filename) {
//	clock_t chrono = clock();
//	clock_t clocks_per_sec = 1000000l;

	if (DEBUG > 0)
		cout << "------------------ Parseando el documento: " << filename << endl;

	unitDoc = new UnitDocument();
	this->document = this->document + 1;
	unitDoc->setDocN(document);
	indexDocument(filename);

	position = 1;
	buffer = "";

	ifstream stream;
	stream.open(filename, fstream::in);

	char c;
	short int tipoChar;
	short int nextState = EMPTY_BUFFER;
	this->setState(EMPTY_BUFFER);

	if (!stream.is_open()) {
		cout << "No se pudo abrir el archivo " << filename << endl;
		stream.clear();
		stream.close();

		return;
	}

	if (!stream.good()) {
		cout << "El archivo " << filename << " se encuentra tan corrupto como Argentina.\n";
		stream.clear();
		stream.close();

		return;
	}

	while (!stream.eof()) {

		stream.get(c);

		if( !stream.eof()) { // miro de nuevo si llegue al final del archivo

			tipoChar = getCharClass(c);
			if (tipoChar == TITTLE) {
				// los tildes se graban -61 y después otro caracter con la vocal
				// o sea ocupan 2 bytes y tengo que leer 2 seguidos.
				stream.get(c);
				c =	removeTittle(c);
				tipoChar = getCharClass(c);
			}
		} else {

			switch (getState()) {
			case READING_WORD:
				indexWord();
				nextState = EMPTY_BUFFER;
				break;
			case READING_NUMBER:
				indexNumber();
				nextState = EMPTY_BUFFER;
				break;
			}
		}

			switch (getState()) {
			case EMPTY_BUFFER:
				switch (tipoChar) {
				case LETTER:
					appendLetter(c);
					nextState = READING_WORD;
					break;
				case DIGIT:
					buffer.push_back(c);
					nextState = READING_NUMBER;
					break;
				default:
					break;
				}
				break;

			case READING_WORD:
				switch (tipoChar) {
				case LETTER:
					appendLetter(c);
					break;
				case DIGIT:
					buffer.push_back(c);
					break;
				case WHITESPACE:
				default:
					indexWord();
					nextState = EMPTY_BUFFER;
					break;
				}
				break;

			case READING_NUMBER:
				switch(tipoChar){
				case DIGIT:
					buffer.push_back(c);
					break;
				case LETTER:
					appendLetter(c);
					nextState = READING_WORD;
					break;
				case WHITESPACE:
				default:
					indexNumber();
					nextState = EMPTY_BUFFER;
					break;
				}
				break;
			}

			if ( nextState != getState())
				this->setState(nextState);
	}

//	clock_t chrono1 = clock();
//	if(SHOW_TIME > 0)
//		cout << "\tParser time: " << ((float)chrono1-(float)chrono)/ clocks_per_sec << " s." <<endl;


	unitDoc->sort();
//	unitDoc->stdOutDoc();
	stream.clear();
	stream.close();

//	if (DEBUG > 1)
//		cout << " ---------------------- Parseo de documento " << filename << " terminado.\n";

	delete unitDoc; //esta linea la tiene que llamar el merge una vez terminado el proceso!
//	clock_t chrono1 = clock();
//	if(SHOW_TIME > 0)
//		cout << "\tDocument total time: " << ((float)chrono1-(float)chrono)/ clocks_per_sec << " s." <<endl;

}

void Parser::parseDirectory(char* dir, string carpetaIdx) {
	carpetaIdx.append(DOC_NAMES_TABLE);
	documents = new ofstream(carpetaIdx.c_str(), std::ios::trunc | std::ios::out);
	if ( DEBUG > 0)
		cout << "-------- indexando repositorio: " << dir << endl << endl;

	struct dirent * entry;
	DIR* directory = opendir(dir);
	while( (entry = readdir(directory)) ) {
		 if( (strcmp(entry->d_name,".")!= 0) && (strcmp(entry->d_name,"..") != 0) && (strstr(entry->d_name,"~") == NULL))
			 if(strstr(entry->d_name, EXTENSION_FILES_TO_INDEX)!= NULL ) {
				 parseDocument(entry->d_name);
			 }
	}

	closedir(directory);
	documents->close();
	delete documents;
}

vector<QueryToken>* Parser::parseQuery(const char* query) {
	position = 0;
	buffer = "";
	vector<QueryToken> *queryTokens = new vector<QueryToken>();
	string strQuery = string(query);
	char c;
	short int tipoChar;
	short int nextState = EMPTY_BUFFER;
	this->setState(EMPTY_BUFFER);

	QueryToken qt;
	qt.pos = 0;
	qt.word = "";
	qt.v_data = NULL;

	for(string::iterator it = strQuery.begin(); it!=strQuery.end(); ++it) {
// cargo el vector con todos los términos que tengo que buscar y en que posición tienen que estar
		c = *it;
		tipoChar = getCharClass(c);

		if (tipoChar == TITTLE) {
			// los tildes se graban -61 y después otro caracter con la vocal
			// o sea ocupan 2 bytes y tengo que leer 2 seguidos.
			c = *(++it);
			c =	removeTittle(c);
			tipoChar = getCharClass(c);
		}


		switch (getState()) {
			case EMPTY_BUFFER:
				switch(tipoChar) {
				case LETTER:
					appendLetter(c);
					nextState = READING_WORD;
					break;
				case DIGIT:
					buffer.push_back(c);
					nextState = READING_NUMBER;
					break;
				default:
					break;
				}
				break;

			case READING_WORD:
				switch(tipoChar) {
				case LETTER:
					appendLetter(c);
					break;
				case DIGIT:
					buffer.push_back(c);
					break;
				case WHITESPACE:
				default:
					qt.pos = getPosition();
					qt.word = getBuffer();
					queryTokens->push_back(qt);
					position++;

					nextState = EMPTY_BUFFER;
					break;
				}
				break;

			case READING_NUMBER:
				switch (tipoChar) {
				case DIGIT:
					buffer.push_back(c);
					break;
				case LETTER:
					appendLetter(c);
					nextState = READING_WORD;
					break;
				case WHITESPACE:
				default:
					if (digitsIndexables(getBuffer())) {
						unitDoc->addTermino( buffer, getPosition());
						qt.pos = getPosition();
						qt.word = getBuffer();
						position++;
					}
					nextState = EMPTY_BUFFER;
					break;
				}
				break;
		}
		if ( nextState != getState())
			this->setState(nextState);

	}

	switch (getState()) {
	case READING_WORD:
		qt.pos = getPosition();
		qt.word = getBuffer();
		queryTokens->push_back(qt);
		position++;

		nextState = EMPTY_BUFFER;
		break;

	case READING_NUMBER:

		if (digitsIndexables(getBuffer())) {
			qt.pos = getPosition();
			qt.word = getBuffer();
			queryTokens->push_back(qt);
			position++;
		}
		nextState = EMPTY_BUFFER;
		break;
	default:
		break;
	}

	return queryTokens;
}

void Parser::indexWord() {
	unitDoc->addTermino( buffer, getPosition());
	position++;
}

void Parser::indexNumber () {
	if (digitsIndexables(getBuffer())) {
		unitDoc->addTermino( buffer, getPosition());
		position++;
	}
}

void Parser::indexDocument(char* filename) {
	string name = string(filename);
	unsigned short int docs = (unsigned short int)document;
	documents->write(reinterpret_cast<char*>(  &(docs)), sizeof(short int));
	uint8_t size = strlen(filename);
	documents->write(reinterpret_cast<char*>(  &(size)), sizeof(char));
	for(unsigned int i=0; i<size; i++)
		documents->write(&(name.at(i)), sizeof(char));
}

void Parser::addWordToSearch() {

}
void Parser::addNumberToSearch() {

}
void Parser::appendLetter (char c) {
	c = tolower(c);
	buffer.push_back(c);
}

bool Parser::digitsIndexables(string number) {
	return ( (MIN_DIGITS_TO_INDEX <= number.length()) && (number.length() <= MAX_DIGITS_TO_INDEX) );
}

char Parser::removeTittle(char c) {
	int auxC = (int) c;
	switch (auxC) {
	case -127:
	case -95:
		return 'a';
		break;
	case -119:
	case -87:
		return 'e';
		break;
	case -83:
	case -115:
		return 'i';
		break;
	case -77:
	case -109:
		return 'o';
		break;
	case -70:
	case -102:
		return 'u';
		break;
	case -79:
	case -111:
		return 'n';
		break;
	}
	return c;
}

void Parser::setState(short int newState) {
	this->state = newState;

	if (state == EMPTY_BUFFER) {
	    buffer.clear();
	    buffer = "";
	}
}

short int Parser::getState() {
	return state;
}

void Parser::setBuffer(string buff) {
  this->buffer = buff;
}

string Parser::getBuffer() {
  return this->buffer;
}

unsigned int Parser::getPosition() {
	return position;
}

Parser::~Parser() {
//	if (documents != NULL) {
//		documents->close();
//		delete documents;
//	}
}

